home *** CD-ROM | disk | FTP | other *** search
/ Die Ultimative Software-P…i Collection 1996 & 1997 / Die Ultimative Software-Pakete CD-ROM fur Atari Collection 1996 & 1997.iso / g / gnu_c / gempp19.zoo / gem++19 / example / example.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-21  |  32.0 KB  |  1,550 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  example
  4. //
  5. //  This file demonstrates many of the features of the gem++ library.
  6. //
  7. /////////////////////////////////////////////////////////////////////////////
  8. //
  9. //  This file is Copyright 1992 by Warwick W. Allison,
  10. //  and is freely distributable providing no charge is made.
  11. //
  12. /////////////////////////////////////////////////////////////////////////////
  13.  
  14. #include "gem++.h"
  15. #include "example.h"
  16. #include "scancode.h"
  17. #include <time.h>
  18. #include <string.h>
  19. #include <support.h>
  20. #include <stdlib.h>
  21. #include <vt52.h>
  22.  
  23. #define FIS_HOLLOW 0
  24. #define FIS_SOLID 1
  25. #define FIS_PATTERN 2
  26. #define FIS_HATCH 3
  27. #define FIS_USER 4
  28.  
  29.  
  30. //
  31. // Demonstrates a simple derived class of GEMobject
  32. //
  33. // Objects of this class will become Checked when clicked upon.
  34. //
  35. class MenuItemToggle : public GEMobject {
  36. public:
  37.     MenuItemToggle(GEMform& form, int RSCindex) :
  38.         GEMobject(form,RSCindex)
  39.     {
  40.     }
  41.  
  42.     GEMfeedback Touch(int x, int y, const GEMevent& e)
  43.     {
  44.         Checked(bool(!Checked()));
  45.         Deselect();
  46.         Redraw();
  47.         return ContinueInteraction;
  48.     }
  49. };
  50.  
  51.  
  52. static char* monthtext[12]={"JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"};
  53.  
  54. //
  55. // Demonstrates GEMtimer and GEMhotform used for popup menus.
  56. //
  57. // This class encapsulates all the RSCindices required to setup
  58. // the clock, so only of of these objects could be created, since
  59. // they use the single graphics in the GEMrsc.  See the GEMringfiw
  60. // below for a more complex example that allows duplicates.
  61. //
  62. // This class has quite a few components...
  63. //
  64. //  - it is a GEMformwindow with no window-parts, so it is a form
  65. //       in a window.
  66. //  - it is a GEMtimer, so its Expire() method is called at the intervals
  67. //       requested with Interval().
  68. //  - it is a GEMobject based on the GRABBER object, so its Touch() method
  69. //       is called when the GRABBER is touched.
  70. //  - it has two GEMtextobjects, timetext and datetext, which we can just
  71. //       treat as if they are char*'s since they have conversion operators.
  72. //  - it has a GEMhotform, popup, used for a pop-up menu.
  73. //  - it has a MenuItemToggle (defined above), gmt, which is in the popup
  74. //       menu, and which we test the Checked() state of when deciding to
  75. //       use local or Greenwich time.
  76. //  - it has a GEMalert, gmjoke, which makes the whole GMT thing worthwhile.
  77. //
  78. // The member functions are described within.
  79. //
  80. class Clock : public GEMformwindow, GEMtimer, GEMobject {
  81. public:
  82.     Clock(GEMactivity& act, const GEMrsc& rsc) :
  83.         GEMformwindow(act,rsc,CLOCK,0),
  84.         GEMtimer(act,0), // Initially interval==0, ie. Expire() immediately.
  85.         GEMobject(*this,GRABBER),
  86.         timetext(*this,TIME),
  87.         datetext(*this,DATE),
  88.         popup(rsc,POPUP),
  89.         gmt(popup,GMT),
  90.         gmjoke(rsc,GMJOKE)
  91.     {
  92.         strcpy(timetext,"    ");
  93.         strcpy(datetext,"     ");
  94.     }
  95.  
  96. private:
  97.  
  98.     // The Update() method gets the local time or Greenwich time, according
  99.     // to whether the gmt GEMobject is Checked.  It then sets the timetext
  100.     // and datetext strings (they are actually GEMtextobjects).  It then
  101.     // Redraws those strings.  Update() is a local member, called by the
  102.     // methods below it.
  103.     //
  104.     void Update()
  105.     {
  106.         time_t ti=time(0);
  107.         tm* T;
  108.         if (gmt.Checked())
  109.             T=gmtime(&ti);
  110.         else
  111.             T=localtime(&ti);
  112.         timetext[0]=T->tm_hour/10 ? T->tm_hour/10+'0' : ' ';
  113.         timetext[1]=T->tm_hour%10+'0';
  114.         timetext[2]=T->tm_min/10+'0';
  115.         timetext[3]=T->tm_min%10+'0';
  116.         strncpy(datetext,monthtext[T->tm_mon],3);
  117.         datetext[3]=T->tm_mday/10 ? T->tm_mday/10+'0' : ' ';
  118.         datetext[4]=T->tm_mday%10+'0';
  119.         if (IsOpen()) {
  120.             timetext.Redraw();
  121.             datetext.Redraw();
  122.         }
  123.     }
  124.  
  125. protected:
  126.  
  127.     // The Expire() method overrides that inherited from GEMtimer.
  128.     // It is called when the interval expires.  Its action it
  129.     // to merely Update the time and wait set the interval to
  130.     // be 60000 milliseconds.  We only need to reset the interval
  131.     // because when we created the object, we requested the interval
  132.     // to be 0 so that the Expire (and hence Update) would be
  133.     // immediate.
  134.     //
  135.     virtual GEMfeedback Expire(const GEMevent&)
  136.     {
  137.         Update();
  138.         Interval(60000); // Every minute, approx.
  139.         return ContinueInteraction;
  140.     }
  141.  
  142.     // The Touch() method overrides that inheritted from GEMobject.
  143.     // It is called whenever the user touches the GRABBER object
  144.     // on which the object was defined.
  145.     //
  146.     // If the touch is made using the right button (button 1), the
  147.     // popup menu is displayed.  If the exitor of the popup is
  148.     // the "close" menuitem, the Close() method inheritted from
  149.     // GEMformwindow is called.  If the exitor is the smiley-face
  150.     // icon (DOGMJOKE, that's Do-GM-joke), the gmjoke alert is
  151.     // popped up.  If the exitor is the "quit" menuitem, EndInteraction
  152.     // is returned, ending the GEMactivity.Do() loop.  Note that
  153.     // the "GMT" menuitem is handled by using the Touch() method of
  154.     // MenuItemToggle as described above.
  155.     //
  156.     // If the touch is not made by the right button, we employ a bit
  157.     // of AES code (hmm, maybe that ugly stuff needs a class) to drag
  158.     // a box the size of the BorderRect of this window, then we
  159.     // GEMformwindow::Move this window to that dragged location.
  160.     //
  161.     virtual GEMfeedback Touch(int x, int y, const GEMevent& e)
  162.     {
  163.         if (e.Button(1)) {
  164.             switch (popup.Do(e.X(),e.Y())) {
  165.              case QCLOCK:
  166.                 Close();
  167.             break; case DOGMJOKE:
  168.                 gmjoke.Alert();
  169.             break; case QPROG:
  170.                 return EndInteraction;
  171.             }
  172.             Update();
  173.             return ContinueInteraction;
  174.         } else {
  175.             int bx,by,bw,bh;
  176.             int nx,ny;
  177.             wind_get(0,WF_WORKXYWH,&bx,&by,&bw,&bh);
  178.             GRect w=BorderRect();
  179.             graf_dragbox(w.g_w,w.g_h,w.g_x,w.g_y,bx,by,bw,bh,&nx,&ny);
  180.  
  181.             GEMformwindow::Move(nx,ny);
  182.  
  183.             return ContinueInteraction;
  184.         }
  185.     }
  186.  
  187. private:
  188.     GEMtextobject timetext,datetext;
  189.     GEMhotform popup;
  190.     MenuItemToggle gmt;
  191.     GEMalert gmjoke;
  192. };
  193.  
  194. //
  195. // Demonstrates GEMvdiobject
  196. //
  197. class GEMliney : public GEMvdiobject {
  198. public:
  199.     GEMliney(GEMform& form, int RSCindex, VDI& vdi) :
  200.         GEMvdiobject(form,RSCindex,vdi)
  201.     {
  202.     }
  203.  
  204. protected:
  205.     virtual void Draw(int x, int y)
  206.         {
  207.             int j=Width() < Height() ? Width() : Height();
  208.             for (int i=0; i<j; i+=3) {
  209.                 vdi.line(x,y+j-i,x+i,y);
  210.             }
  211.         }
  212. };
  213.  
  214. //
  215. // Demonstrates GEMvdiobject, with special "Selected" state display.
  216. //
  217. class GEMellipse : public GEMvdiobject {
  218. public:
  219.     GEMellipse(GEMform& form, int RSCindex, VDI& vdi) :
  220.         GEMvdiobject(form,RSCindex,vdi)
  221.     {
  222.     }
  223.  
  224. protected:
  225.     virtual void Draw(int x, int y)
  226.         {
  227.             vdi.sf_interior(FIS_PATTERN);
  228.             if (Selected()) vdi.sf_style(20);
  229.             else vdi.sf_style(10);
  230.             vdi.ellipse(x+Width()/2,y+Height()/2,Width()/2,Height()/2);
  231.         }
  232. };
  233.  
  234. //
  235. // Demonstrates that GEMuserobjects (and GEMvdiobjects) retain
  236. // the features of the object that is having its display representation
  237. // redefined.
  238. //
  239. class GEMroundbutton : public GEMvdiobject {
  240. private:
  241.     int texth;
  242.  
  243. public:
  244.     GEMroundbutton(GEMform& f, int RSCindex, VDI& v) :
  245.         GEMvdiobject(f,RSCindex,v)
  246.     {
  247.         int j;
  248.         graf_handle(&j,&texth,&j,&j);
  249.     }
  250.  
  251. protected:
  252.     virtual void Draw(int x, int y)
  253.         {
  254.             if (Selected()) vdi.sf_interior(1);
  255.             else vdi.sf_interior(0);
  256.             vdi.rfbox(x,y,x+Width()-1,y+Height()-1);
  257.             vdi.swr_mode(MD_XOR);
  258.             int j;
  259.             vdi.st_alignment(1,3,&j,&j); // Centre-Bottom aligned
  260.             vdi.gtext(x+Width()/2,y+(Height()+texth)/2-1,Text());
  261.             vdi.swr_mode(MD_REPLACE);
  262.         }
  263. };
  264.  
  265.  
  266. //
  267. // Demonstrates various GEMobjects, and the GEMformiconwindow
  268. //
  269. class Various : public GEMformiconwindow {
  270. public:
  271.     Various(GEMactivity& in, const GEMrsc& rsc) :
  272.         GEMformiconwindow(in,rsc,VARIOUS,VARIOUSI),
  273.         image("example.img"),
  274.         picture(*this,PIC,image),
  275.         panner(*this,PANKNOB,PANRACK,PANLEFT,PANRIGHT,PANUP,PANDOWN),
  276.         vertslider(*this,VKNOB,VRACK,UP,DOWN),
  277.         horzslider(*this,HKNOB,HRACK,LEFT,RIGHT),
  278.         vdi(),
  279.         ellipse(*this,ELLIPSE,vdi),
  280.         liney(*this,LINEY,vdi),
  281.         r1(*this,RBUT1,vdi),
  282.         r2(*this,RBUT2,vdi)
  283.     {
  284.         SetName(" Various GEM++ objects ");
  285.     }
  286.  
  287. private:
  288.     IMG image;
  289.     GEMimageobject picture;
  290.     GEMslider panner;
  291.     GEMslider vertslider;
  292.     GEMslider horzslider;
  293.     VDI vdi;
  294.     GEMellipse ellipse;
  295.     GEMliney liney;
  296.     GEMroundbutton r1;
  297.     GEMroundbutton r2;
  298. };
  299.  
  300.  
  301. class GEMvditextobject : public GEMvdiobject {
  302. public:
  303.     GEMvditextobject(GEMform& f, int RSCindex, VDI& v) :
  304.         GEMvdiobject(f,RSCindex,v)
  305.     {
  306.     }
  307.  
  308.     virtual void Draw(int x, int y)
  309.     {
  310.         // Assume vdi is all setup.
  311.         vdi.r_recfl(x,y,x+Width()-1,y+Height()-1);
  312.         vdi.gtext(x,y,Text());
  313.     }
  314. };
  315.  
  316. // A scrolling list of [selectable] text lines.
  317. //
  318. class ScrollingTextList : public GEMslider {
  319. public:
  320.     ScrollingTextList(GEMform& form, int RSCbox,
  321.      int RSCknob, int RSCrack, int RSCminus, int RSCplus,
  322.      int RSCfirstline, int RSClastline) :
  323.         GEMslider(form,RSCknob,RSCrack,RSCminus,RSCplus),
  324.         line0(RSCfirstline),
  325.         lineN(RSClastline),
  326.         linebox(RSCbox)
  327.     {
  328.         SetTotalLines(lineN-line0+1);
  329.         SetVisibleLines(lineN-line0+1);
  330.     }
  331.  
  332.     int LineToRSCindex(int line)
  333.     {
  334.         int RSCindex=line-TopLine()+FONTNAME0;
  335.  
  336.         if (RSCindex>=line0 && RSCindex<=lineN) {
  337.             return RSCindex;
  338.         } else {
  339.             return -1;
  340.         }
  341.     }
  342.  
  343.     int RSCindexToLine(int RSCindex)
  344.     {
  345.         return RSCindex-line0+TopLine();
  346.     }
  347.  
  348.     void RedrawLine(int line)
  349.     {
  350.         int RSCindex=LineToRSCindex(line);
  351.  
  352.         if (RSCindex>=0) {
  353.             form.RedrawObject(RSCindex);
  354.         }
  355.     }
  356.  
  357.     virtual void VFlush()
  358.     {
  359.         int lineindex=TopLine();
  360.  
  361.         for (int i=line0; i<=lineN; i++) {
  362.             if (lineindex<NumberOfLines()) {
  363.                 strcpy(form[i].Text(),Line(lineindex));
  364.                 if (SelectedLine(lineindex)) {
  365.                     form[i].Select();
  366.                 } else {
  367.                     form[i].Deselect();
  368.                 }
  369.             } else {
  370.                 strcpy(form[i].Text()," ");
  371.                 form[i].Deselect();
  372.             }
  373.  
  374.             lineindex++;
  375.         }
  376.  
  377.         form.RedrawObject(linebox);
  378.  
  379.         GEMslider::VFlush();
  380.     }
  381.  
  382. protected:
  383.     // Override these to get an actual scrolling textbox...
  384.     virtual int NumberOfLines()=0;
  385.     virtual char* Line(int i)=0;
  386.  
  387.     // Override this if some of the lines are to be selectable.
  388.     virtual bool SelectedLine(int line)
  389.     {
  390.         return FALSE;
  391.     }
  392.  
  393. private:
  394.     int line0, lineN, linebox;
  395. };
  396.  
  397. class ScrollingFontList : public ScrollingTextList {
  398. public:
  399.     ScrollingFontList(GEMform& form, int RSCbox,
  400.      int RSCknob, int RSCrack, int RSCminus, int RSCplus,
  401.      int RSCfirstline, int RSClastline, VDI& v) :
  402.         ScrollingTextList(form, RSCbox, RSCknob, RSCrack, RSCminus, RSCplus, RSCfirstline, RSClastline),
  403.         selectedfont(0),
  404.         fontlist(v)
  405.     {
  406.         SetTopLine(0);
  407.         if (fontlist.NumberOfFonts() < VisibleLines()) {
  408.             SetVisibleLines(fontlist.NumberOfFonts());
  409.         }
  410.         SetTotalLines(fontlist.NumberOfFonts());
  411.     }
  412.  
  413.     GEMfont SelectedFont()
  414.     {
  415.         return fontlist.Font(selectedfont);
  416.     }
  417.  
  418.     void LineClicked(int RSCindex)
  419.     {
  420.         int newselectedfont=RSCindexToLine(RSCindex);
  421.  
  422.         if (newselectedfont<fontlist.NumberOfFonts()) {
  423.             int RSCold=LineToRSCindex(selectedfont);
  424.             if (RSCold>=0) {
  425.                 form[RSCold].Deselect();
  426.                 form.RedrawObject(RSCold);
  427.             }
  428.             selectedfont=newselectedfont;
  429.             form[RSCindex].Select();
  430.             form.RedrawObject(RSCindex);
  431.         }
  432.     }
  433.  
  434. protected:
  435.     virtual int NumberOfLines()
  436.     {
  437.         return fontlist.NumberOfFonts();
  438.     }
  439.  
  440.     virtual char* Line(int i)
  441.     {
  442.         return fontlist.FontName(i);
  443.     }
  444.  
  445.     virtual bool SelectedLine(int line)
  446.     {
  447.         return line==selectedfont;
  448.     }
  449.  
  450. private:
  451.     int selectedfont;
  452.     GEMfontlist fontlist;
  453. };
  454.  
  455. //
  456. // Demonstrates how to use events rather than busy-waiting.
  457. //
  458. static void WaitForNoButton()
  459. {
  460.     GEMevent event;
  461.     if (event.Button()) {
  462.         event.Button(1,0); // wait for left button release
  463.         event.Get(MU_BUTTON);
  464.     }
  465. }
  466.  
  467. //
  468. // Demonstrates GEMfonts.
  469. //
  470. class FontWindow : public GEMformiconwindow {
  471. public:
  472.     FontWindow(GEMactivity& in, const GEMrsc& rsc) :
  473.         GEMformiconwindow(in,rsc,FONTS,FONTSI),
  474.         vdi(),
  475.         fontlist(*this,FONTNAMEBOX,FONTKNOB,FONTRACK,FONTMINUS,FONTPLUS,
  476.             FONTNAME0,FONTNAMEN,(vdi.st_load_fonts(),vdi)),
  477.         font(vdi,fontlist.SelectedFont()),
  478.         exampletext(*this,EXAMPLETEXT,vdi)
  479.     {
  480.         SetName(" Font Selection ");
  481.         exampletext.SetText("Quick brown foxes jump over the lazy dog.");
  482.         int j;
  483.         vdi.st_alignment(0,5,&j,&j); // Left-Top aligned
  484.         vdi.sf_interior(0);
  485.         fontlist.VFlush();
  486.         font.Use();
  487.         _itoa(font.PointSize(),Object(FONTPOINT).Text(),10);
  488.     }
  489.  
  490.     const GEMfont& Chosen()
  491.     {
  492.         return font;
  493.     }
  494.  
  495.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  496.     {
  497.         bool fontchanged=FALSE;
  498.         GEMformwindow::DoItem(item,e);
  499.  
  500.         if (item>=FONTNAME0 && item <=FONTNAMEN) {
  501.             fontlist.LineClicked(item);
  502.             GEMfont selectedfont=fontlist.SelectedFont();
  503.             font.Code(selectedfont.Code());
  504.             font.Smaller();
  505.             font.Larger();
  506.             fontchanged=TRUE;
  507.         }
  508.  
  509.         if (item==FONTPOINT) {
  510.             font.PointSize(atoi(Object(FONTPOINT).Text()));
  511.             fontchanged=TRUE;
  512.         }
  513.  
  514.         if (font.ArbitrarilySizable()) {
  515.             switch (item) {
  516.              case FONTPTPLUS10:
  517.                 font.PointSize(font.PointSize()+10);
  518.                 fontchanged=TRUE;
  519.             break; case FONTPTMINUS10:
  520.                 font.PointSize(font.PointSize()-10);
  521.                 fontchanged=TRUE;
  522.             break; case FONTPTPLUS1:
  523.                 font.PointSize(font.PointSize()+1);
  524.                 fontchanged=TRUE;
  525.             break; case FONTPTMINUS1:
  526.                 font.PointSize(font.PointSize()-1);
  527.                 fontchanged=TRUE;
  528.             }
  529.         } else {
  530.             switch (item) {
  531.              case FONTPTPLUS10:
  532.                 font.Larger();
  533.                 fontchanged=TRUE;
  534.             break; case FONTPTMINUS10:
  535.                 font.Smaller();
  536.                 fontchanged=TRUE;
  537.             break; case FONTPTPLUS1:
  538.                 font.Larger();
  539.                 fontchanged=TRUE;
  540.             break; case FONTPTMINUS1:
  541.                 font.Smaller();
  542.                 fontchanged=TRUE;
  543.             }
  544.         }
  545.  
  546.         if (fontchanged) {
  547.             font.Use();
  548.             _itoa(font.PointSize(),Object(FONTPOINT).Text(),10);
  549.             RedrawObject(FONTPOINT);
  550.             exampletext.Redraw();
  551.         }
  552.  
  553.         WaitForNoButton();
  554.  
  555.         return ContinueInteraction;
  556.     }
  557.  
  558. private:
  559.     VDI vdi;
  560.     ScrollingFontList fontlist;
  561.     GEMfont font;
  562.     GEMvditextobject exampletext;
  563. };
  564.  
  565.  
  566. //
  567. // Demonstrates a sophisticated duplicatable window with shared objects
  568. //
  569. class GEMringfiw : public GEMformiconwindow {
  570. private:
  571.     GEMringfiw*& head;
  572.     GEMringfiw* next;
  573.     GEMringfiw* prev;
  574.  
  575. protected:
  576.     virtual GEMfeedback UserClosed() {
  577.         GEMformwindow::Close();
  578.         delete this;
  579.         return ContinueInteraction;
  580.     }
  581.  
  582.     GEMringfiw(GEMactivity& in, const GEMrsc& rsc, int RSCform, int RSCicon, GEMringfiw*& h) :
  583.         GEMformiconwindow(in,rsc,RSCform,RSCicon),
  584.         head(h)
  585.     {
  586.         next=prev=this;
  587.     }
  588.  
  589.     GEMringfiw(GEMringfiw& copy, GEMringfiw*& h) :
  590.         GEMformiconwindow(copy),
  591.         next(©), prev(copy.prev),
  592.         head(h)
  593.     {
  594.         next->prev=this;
  595.         prev->next=this;
  596.     }
  597.  
  598.     ~GEMringfiw()
  599.     {
  600.         if (next==this) {
  601.             head=0;
  602.         } else {
  603.             if (head==this) head=next;
  604.             prev->next=next;
  605.             next->prev=prev;
  606.         }
  607.     }
  608.  
  609. public:
  610.     static bool OpenNew(GEMactivity& act, const GEMrsc& rsc, int RSCform, int RSCicon, GEMringfiw*& head)
  611.     {
  612.         GEMringfiw* newhead;
  613.  
  614.         if (head)
  615.             newhead=new GEMringfiw(*head,head);
  616.         else
  617.             newhead=new GEMringfiw(act,rsc,RSCform,RSCicon,head);
  618.  
  619.         head=newhead;
  620.         head->Open();
  621.         if (head->IsOpen()) {
  622.             return TRUE;
  623.         } else {
  624.             delete newhead;
  625.             return FALSE;
  626.         }
  627.     }
  628.  
  629.     static void DeleteAll(GEMringfiw*& head)
  630.     {
  631.         while (head) {
  632.             delete head;
  633.         }
  634.     }
  635. };
  636.  
  637.  
  638. //
  639. // Demonstrates flying dialog boxes.
  640. //
  641. class GEMflyform : public GEMform {
  642. public:
  643.     GEMflyform(GEMrsc& rsc, int RSCindexform, int RSCindexknob=0, bool opaq=FALSE) :
  644.         GEMform(rsc,RSCindexform),
  645.         opaque(opaq),
  646.         knob(RSCindexknob)
  647.     {
  648.     }
  649.  
  650. protected:
  651.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  652.     {
  653.         if (item==knob) Fly(opaque);
  654.  
  655.         return GEMform::DoItem(item,e);
  656.     }
  657.  
  658.     void Opaque(bool opaq=TRUE)
  659.     {
  660.         opaque=opaq;
  661.     }
  662.  
  663. private:
  664.     bool opaque;
  665.     int knob;
  666. };
  667.  
  668. class Flying : public GEMflyform {
  669. public:
  670.     Flying(GEMrsc& rsc) :
  671.         GEMflyform(rsc,FLYING,FLYKNOB)
  672.     {
  673.         Opaque(Object(OPAQUE).Selected());
  674.     }
  675.  
  676. protected:
  677.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  678.     {
  679.         if (item==OPAQUE) Opaque(Object(OPAQUE).Selected());
  680.  
  681.         return GEMflyform::DoItem(item,e);
  682.     }
  683. };
  684.  
  685.  
  686. class ColourSlider : public GEMslider {
  687. // A ColourSlider is a simple slider (no arrowers) that shows the current
  688. // value in the knob as text and colour.
  689. //
  690. // TODO: Auto-detect orientation.  Currently ONLY HORIZONTAL.
  691. //
  692. public:
  693.     ColourSlider(GEMform& form, int rack, int minvalue, int maxvalue) :
  694.         GEMslider(form,rack+1,rack)
  695.     {
  696.         SetTotalColumns(maxvalue-minvalue+1);
  697.         SetVisibleColumns(2);
  698.         SetLeftColumn(0);
  699.     }
  700.  
  701.     virtual int HPageAmount()
  702.     {
  703.         return 1;
  704.     }
  705.  
  706.     void HFlush()
  707.     {
  708.         int v=Value();
  709.         char* text=form[FirstChild()].Text();
  710.         if (v<10) {
  711.             text[0]=v+'0';
  712.             text[1]=0;
  713.         } else {
  714.             text[0]=v/10+'0';
  715.             text[1]=v%10+'0';
  716.             text[2]=0;
  717.         }
  718.  
  719.         form[FirstChild()].BackCol(Value());
  720.         if (Value()==0) {
  721.             form[FirstChild()].ForeCol(1);
  722.         } else {
  723.             form[FirstChild()].ForeCol(0);
  724.         }
  725.         GEMslider::HFlush();
  726.     }
  727.  
  728.     int Value()
  729.     {
  730.         return LeftColumn();
  731.     }
  732.  
  733.     void Value(int v)
  734.     {
  735.         SetLeftColumn(v);
  736.     }
  737. };
  738.  
  739. class PartsForm : public GEMformiconwindow {
  740. public:
  741.     PartsForm(GEMactivity& in, const GEMrsc& rsc) :
  742.         GEMformiconwindow(in,rsc,BIGFORM,BIGFORMI,
  743.             /* ALL */ NAME|CLOSER|FULLER|MOVER|SIZER|INFO|UPARROW|DNARROW|VSLIDE|LFARROW|RTARROW|HSLIDE),
  744.         borderslider(*this,BORDERRACK,0,15),
  745.         textslider(*this,TEXTRACK,0,15),
  746.         fillslider(*this,FILLRACK,0,15)
  747.     {
  748.         Resize(200,200);
  749.         GEMform enabled(rsc,WCACTIVE);
  750.         GEMform disabled(rsc,WCINACTIVE);
  751.         SetPartColours(enabled,disabled);
  752.         SetName(" Big Form ");
  753.  
  754.         for (int p=PART0; p<=PARTN && !Object(p).Selected(); p++)
  755.             ;
  756.  
  757.         if (p>PARTN) {
  758.             p=PART0;
  759.             Object(p).Select();
  760.         }
  761.  
  762.         SetCurrentPart(p-PART0);
  763.     }
  764.  
  765.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  766.     {
  767.         GEMfeedback result=ContinueInteraction;
  768.  
  769.         if (item==MODEBUTTON) {
  770.             Object(MODEBUTTON).Transparent(bool(!Object(MODEBUTTON).Transparent()));
  771.             colour.bits.textmode=Object(MODEBUTTON).Transparent();
  772.             RedrawObject(MODEBUTTON);
  773.         } else if (item>=PART0 && item<=PARTN) {
  774.             SetCurrentPart(item-PART0);
  775.         } else if (item>=FILL0 && item<=FILLN) {
  776.             SelectPattern(item-FILL0);
  777.         } else if (item==INACTIVE) {
  778.             RedrawObject(Parent(INACTIVE));
  779.             result=RedrawMyParent;
  780.             SetCurrentPart(currentpart);
  781.         } else if (item==ACTIVE) {
  782.             SetCurrentPart(currentpart);
  783.         } else {
  784.             // The sliders are real objects.
  785.             result=GEMformiconwindow::DoItem(item,e);
  786.  
  787.             switch (item) {
  788.              case BORDERKNOB: case BORDERRACK:
  789.                 colour.bits.framecol=borderslider.Value();
  790.             break; case TEXTKNOB: case TEXTRACK:
  791.                 colour.bits.textcol=textslider.Value();
  792.             break; case FILLKNOB: case FILLRACK:
  793.                 colour.bits.interiorcol=fillslider.Value();
  794.             }
  795.         }
  796.  
  797.         if (Object(ACTIVE).Selected()) {
  798.             SetPartColour(currentpart,colour.packed,-1);
  799.         } else {
  800.             SetPartColour(currentpart,-1,colour.packed);
  801.         }
  802.  
  803.         return result;
  804.     }
  805.  
  806. private:
  807.     ColourSlider borderslider,textslider,fillslider;
  808.  
  809.     void SetCurrentPart(int part)
  810.     {
  811.         currentpart=part;
  812.  
  813.         if (Object(ACTIVE).Selected()) {
  814.             colour.packed=PartColour(part,TRUE);
  815.         } else {
  816.             colour.packed=PartColour(part,FALSE);
  817.         }
  818.  
  819.         borderslider.Value(colour.bits.framecol);
  820.         textslider.Value(colour.bits.textcol);
  821.  
  822.         Object(MODEBUTTON).Transparent(bool(colour.bits.textmode));
  823.  
  824.         SelectPattern(colour.bits.fillpattern);
  825.  
  826.         fillslider.Value(colour.bits.interiorcol);
  827.     }
  828.  
  829.     void SelectPattern(int p)
  830.     {
  831.         if (p<0) p=0;
  832.         else if (p+FILL0>FILLN) p=FILLN-FILL0;
  833.  
  834.         colour.bits.fillpattern=p;
  835.  
  836.         for (int i=FILL0; i<=FILLN; i++) {
  837.             if (*(Object(i).Text())=='\010') {
  838.                 Object(i).SetText(" ");
  839.                 RedrawObject(i);
  840.                 break;
  841.             }
  842.         }
  843.  
  844.         Object(FILL0+p).SetText("\010");
  845.         RedrawObject(FILL0+p);
  846.     }
  847.  
  848.     int currentpart;
  849.  
  850.     union {
  851.         short packed;
  852.         struct {
  853.             unsigned short framecol    :  4;
  854.             unsigned short textcol     :  4;
  855.             unsigned short textmode    :  1;
  856.             unsigned short fillpattern :  3;
  857.             unsigned short interiorcol :  4;
  858.         } bits;
  859.     } colour;
  860. };
  861.  
  862. class Shape {
  863. public:
  864.     static void SetVDI(VDI& v)
  865.     // Set the device on which all Shapes are drawn.
  866.     {
  867.         vdi=&v;
  868.     }
  869.  
  870.     Shape(Shape* n=0) :
  871.         next(n)
  872.     {
  873.     }
  874.  
  875.     void Append(Shape* sh)
  876.     {
  877.         Shape* cursor=this;
  878.         while (cursor->next) {
  879.             cursor=cursor->next;
  880.         }
  881.         cursor->next=sh;
  882.     }
  883.  
  884.     Shape* Next() { return next; }
  885.  
  886.     virtual void Draw(int xoff, int yoff)=0;
  887.  
  888.     virtual void GetExtent(GRect& extent)=0;
  889.  
  890.     virtual bool Touches(int x,int y)=0;
  891.  
  892.     Shape* Remove(Shape* sh)
  893.     // Remove sh from this list, returning resulting list.
  894.     {
  895.         if (sh==this) {
  896.             Shape* result=next;
  897.             next=0;
  898.  
  899.             return result;
  900.         } else {
  901.             if (next)
  902.                 next=next->Remove(sh);
  903.             return this;
  904.         }
  905.     }
  906.  
  907.     Shape* FindLast(int x,int y)
  908.     // Return the last shape touching (x,y).
  909.     {
  910.         Shape* last=0;
  911.         Shape* cursor=this;
  912.  
  913.         while (cursor) {
  914.             if (cursor->Touches(x,y))
  915.                 last=cursor;
  916.             cursor=cursor->next;
  917.         }
  918.  
  919.         return last;
  920.     }
  921.  
  922. protected:
  923.     VDI& Device()
  924.     {
  925.         return *vdi;
  926.     }
  927.  
  928. private:
  929.     static VDI* vdi;
  930.     Shape* next;
  931. };
  932.  
  933. VDI* Shape::vdi=0;
  934.  
  935. class CircleShape : public Shape {
  936. public:
  937.     CircleShape(int xpos, int ypos, int radius, Shape* next=0) :
  938.         Shape(next),
  939.         x(xpos),y(ypos),r(radius)
  940.     {
  941.     }
  942.  
  943.     virtual void Draw(int xoff, int yoff)
  944.     {
  945.         Device().circle(x+xoff,y+yoff,r);
  946.     }
  947.  
  948.     virtual void GetExtent(GRect& extent)
  949.     {
  950.         extent.g_x=x-r;
  951.         extent.g_y=y-r;
  952.         extent.g_w=r*2+1;
  953.         extent.g_h=r*2+1;
  954.     }
  955.  
  956.     virtual bool Touches(int tx,int ty)
  957.     {
  958.         if (tx<x-r || tx>x+r || ty<y-r || ty>y+r) return FALSE;
  959.  
  960.         int dx=abs(tx-x);
  961.         int dy=abs(ty-y);
  962.  
  963.         // Distance formula:  sqrt(dx**2 + dy**2) < r
  964.         return dx*dx+dy*dy <= r*r;
  965.     }
  966.  
  967. private:
  968.     int x,y,r;
  969. };
  970.  
  971. class RectangleShape : public Shape {
  972. public:
  973.     RectangleShape(int xpos, int ypos, int width, int height, Shape* next=0) :
  974.         Shape(next),
  975.         x(xpos),y(ypos),w(width),h(height)
  976.     {
  977.     }
  978.  
  979.     virtual void Draw(int xoff, int yoff)
  980.     {
  981.         Device().bar(x+xoff,y+yoff,x+xoff+w-1,y+yoff+h-1);
  982.     }
  983.  
  984.     virtual void GetExtent(GRect& extent)
  985.     {
  986.         extent.g_x=x;
  987.         extent.g_y=y;
  988.         extent.g_w=w;
  989.         extent.g_h=h;
  990.     }
  991.  
  992.     virtual bool Touches(int tx,int ty)
  993.     {
  994.         return tx>=x && tx<x+w && ty>=y && ty<y+h;
  995.     }
  996.  
  997. private:
  998.     int x,y,w,h;
  999. };
  1000.  
  1001. class TextShape : public Shape {
  1002. public:
  1003.     TextShape(int xpos, int ypos, const char* text, const GEMfont& fnt, Shape* next=0) :
  1004.         Shape(next),
  1005.         x(xpos),y(ypos),txt(strdup(text)),
  1006.         font(Device(),fnt)
  1007.     {
  1008.     }
  1009.  
  1010.     virtual ~TextShape()
  1011.     {
  1012.         delete txt;
  1013.     }
  1014.  
  1015.     virtual void Draw(int xoff, int yoff)
  1016.     {
  1017.         font.Use();
  1018.         Device().gtext(x+xoff,y+yoff,txt);
  1019.     }
  1020.  
  1021.     virtual void GetExtent(GRect& extent)
  1022.     {
  1023.         // NB:  qt_extent() does not take alignment into account.
  1024.         //      (ie. we are assuming alignement is Top-Left)
  1025.         int txt_extent[8];
  1026.         font.Use();
  1027.         Device().qt_extent(txt,txt_extent);
  1028.  
  1029.         extent.g_x=x+txt_extent[0];
  1030.         extent.g_y=y+txt_extent[1];
  1031.         extent.g_w=txt_extent[2]-txt_extent[0]+1;
  1032.         extent.g_h=txt_extent[5]-txt_extent[3]+1;
  1033.     }
  1034.  
  1035.     virtual bool Touches(int tx,int ty)
  1036.     {
  1037.         GRect r;
  1038.         GetExtent(r);
  1039.  
  1040.         return tx>=r.g_x && tx<r.g_x+r.g_w && ty>=r.g_y && ty<r.g_y+r.g_h;
  1041.     }
  1042.  
  1043. private:
  1044.     int x,y;
  1045.     char* txt;
  1046.     GEMfont font;
  1047. };
  1048.  
  1049. class Canvas : public GEMcanvas {
  1050. public:
  1051.     Canvas(GEMform& form, int RSCindex) :
  1052.         GEMcanvas(form,RSCindex),
  1053.         shape_list(0)
  1054.     {
  1055.         st_alignment(0,5);
  1056.         sf_interior(FIS_HOLLOW);
  1057.     }
  1058.  
  1059.     void RedrawVirtualArea(GRect& rect)
  1060.     {
  1061.         form.RedrawObject(myindex,rect.g_x-CanvasX(),rect.g_y-CanvasY(),rect.g_w,rect.g_h);
  1062.     }
  1063.  
  1064.     void AppendShape(Shape* sh)
  1065.     {
  1066.         if (shape_list)
  1067.             shape_list->Append(sh);
  1068.         else
  1069.             shape_list=sh;
  1070.  
  1071.         GRect extent;
  1072.         sh->GetExtent(extent);
  1073.         RedrawVirtualArea(extent);
  1074.     }
  1075.  
  1076.     Shape* FindShape(int x, int y)
  1077.     {
  1078.         if (shape_list)
  1079.             return shape_list->FindLast(x,y);
  1080.         else
  1081.             return 0;
  1082.     }
  1083.  
  1084.     void DeleteShape(Shape* sh)
  1085.     {
  1086.         if (shape_list) {
  1087.             GRect extent;
  1088.             sh->GetExtent(extent);
  1089.  
  1090.             shape_list=shape_list->Remove(sh);
  1091.  
  1092.             RedrawVirtualArea(extent);
  1093.  
  1094.             delete sh;
  1095.         }
  1096.     }
  1097.  
  1098.     void Circle(int x, int y, int radius)
  1099.     {
  1100.         AppendShape(new CircleShape(x,y,radius));
  1101.     }
  1102.  
  1103.     void Rectangle(int x, int y, int w, int h)
  1104.     {
  1105.         AppendShape(new RectangleShape(x,y,w,h));
  1106.     }
  1107.  
  1108.     void Text(int x, int y, char* text, const GEMfont& fnt)
  1109.     {
  1110.         GEMfont to_use(*this,fnt);
  1111.         AppendShape(new TextShape(x,y,text,to_use));
  1112.     }
  1113.  
  1114.     virtual void DrawAt(int x, int y, const GRect& ignored)
  1115.     {
  1116.         // Just rely on VDI clipping to decide what to draw.
  1117.         DrawAll(x,y);
  1118.     }
  1119.  
  1120.     void DrawAll(int x, int y)
  1121.     {
  1122.         for (Shape* sh=shape_list; sh; sh=sh->Next()) {
  1123.             sh->Draw(x,y);
  1124.         }
  1125.     }
  1126.  
  1127.     void DrawOn(VDI& v)
  1128.     {
  1129.         Shape::SetVDI(v);
  1130.         DrawAll(0,0);
  1131.         Shape::SetVDI(*this);
  1132.     }
  1133.  
  1134.     void WriteMetafile(char* filename)
  1135.     {
  1136.         VDI metafile(filename);
  1137.  
  1138.         GEMchangearea bb; // Bounding Box
  1139.  
  1140.         for (Shape* sh=shape_list; sh; sh=sh->Next()) {
  1141.             GRect extent;
  1142.             sh->GetExtent(extent);
  1143.             bb.Include(extent);
  1144.         }
  1145.  
  1146.         metafile.m_pagesize(bb.g_w*PixelWidth()/100,bb.g_h*PixelHeight()/100);
  1147.         metafile.meta_extents(bb.g_x,bb.g_y,bb.g_x+bb.g_w-1,bb.g_y+bb.g_h-1);
  1148.  
  1149.         DrawOn(metafile);
  1150.     }
  1151.  
  1152. private:
  1153.     Shape* shape_list;
  1154. };
  1155.  
  1156. static void Untouch(int timeout)
  1157. {
  1158.     GEMevent event;
  1159.     if (event.Button()) {
  1160.         event.Interval(timeout); // Wait for timeout
  1161.         event.Button(1,0); // or leftbutton release
  1162.         event.Get(MU_TIMER|MU_BUTTON);
  1163.     }
  1164. }
  1165.  
  1166. // A trivial sliding object.
  1167. class GEMslidable : public GEMobject {
  1168. public:
  1169.     GEMslidable(GEMform& form, int RSCindex) :
  1170.         GEMobject(form,RSCindex)
  1171.     {
  1172.     }
  1173.  
  1174.     virtual GEMfeedback Touch(int x, int y, const GEMevent&)
  1175.     {
  1176.         GEMobject parent(form,form.Parent(myindex));
  1177.  
  1178.         int tx,ty;
  1179.         parent.GetAbsoluteXY(tx,ty);
  1180.  
  1181.         int ox,oy,nx,ny;
  1182.         ox=tx+X();
  1183.         oy=ty+Y();
  1184.  
  1185.         graf_dragbox(Width(),Height(),ox,oy,
  1186.             tx,ty,parent.Width(),parent.Height(),&nx,&ny);
  1187.  
  1188.         // Redraw parent with this hidden
  1189.         HideTree(TRUE);
  1190.  
  1191.         int cx,cy;
  1192.         GetAbsoluteXY(cx,cy);
  1193.  
  1194.         form.RedrawObject(form.Parent(myindex), cx-tx, cy-ty,
  1195.             Width(), Height());
  1196.  
  1197.         MoveTo(nx-tx,ny-ty);
  1198.  
  1199.         HideTree(FALSE);
  1200.  
  1201.         Redraw();
  1202.  
  1203.         return ContinueInteraction;
  1204.     }
  1205. };
  1206.  
  1207. class CanvasWindow : public GEMformiconwindow {
  1208. public:
  1209.     CanvasWindow(GEMactivity& in, const GEMrsc& rsc, FontWindow& fntselector) :
  1210.         GEMformiconwindow(in,rsc,CANVAS,CANVASI),
  1211.         canvas(*this,CNVBOX),
  1212.         speed(*this,CNVKNOB,CNVRACK),
  1213.         parameter(*this,CNVPARM),
  1214.         cursor(*this,CNVCURSOR),
  1215.         canvashelp(rsc,CANVASHELP),
  1216.         canvasphelp(in,rsc,CANVASPHELP),
  1217.         fontselector(fntselector)
  1218.     {
  1219.         canvas.st_load_fonts();
  1220.  
  1221.         SetName(" Canvas ");
  1222.         canvasphelp.SetName(" Canvas - parameters ");
  1223.  
  1224.         Shape::SetVDI(canvas);
  1225.     }
  1226.  
  1227.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  1228.     {
  1229.         int cursor_x=canvas.CanvasX()+Object(CNVCURSOR).X()+7;
  1230.         int cursor_y=canvas.CanvasY()+Object(CNVCURSOR).Y()+7;
  1231.  
  1232.         switch (item) {
  1233.          case CNVUP: case CNVDOWN: case CNVLEFT: case CNVRIGHT:
  1234.             // Redraw CNVBOX with CNVCURSOR hidden
  1235.             Object(CNVCURSOR).HideTree(TRUE);
  1236.  
  1237.             // XXX This should be provided by GEMform
  1238.             int cx,cy,bx,by;
  1239.             objc_offset(Obj,CNVCURSOR,&cx,&cy);
  1240.             objc_offset(Obj,CNVBOX,&bx,&by);
  1241.             RedrawObject(CNVBOX,cx-bx,cy-by,
  1242.                 Object(CNVCURSOR).Width(),
  1243.                 Object(CNVCURSOR).Height());
  1244.  
  1245.             int delay=speed.TotalColumns()-speed.LeftColumn()-speed.VisibleColumns();
  1246.             int scroll_amount=1;
  1247.  
  1248.             // Delay 0..7 is implemented using scroll amount 16..2, no delay.
  1249.             // Delay 8.. is implemented using scroll amount of 1 and time delay.
  1250.             //
  1251.             // Why?  Because it looks nice.
  1252.             //
  1253.             if (delay < 8) {
  1254.                 scroll_amount=(8-delay)*2;
  1255.                 delay=0;
  1256.             } else {
  1257.                 delay-=7;
  1258.             }
  1259.  
  1260.             switch (item) {
  1261.              case CNVUP:
  1262.                 canvas.VScroll(-scroll_amount);
  1263.             break; case CNVDOWN:
  1264.                 canvas.VScroll(+scroll_amount);
  1265.             break; case CNVLEFT:
  1266.                 canvas.HScroll(-scroll_amount);
  1267.             break; case CNVRIGHT:
  1268.                 canvas.HScroll(+scroll_amount);
  1269.             }
  1270.  
  1271.             if (delay) {
  1272.                 Untouch(delay*5);
  1273.             }
  1274.  
  1275.             // Replace cursor
  1276.             Object(CNVCURSOR).HideTree(FALSE);
  1277.             RedrawObject(CNVCURSOR);
  1278.         break; case CNVCIRCLE:
  1279.             int radius=atoi(parameter);
  1280.             if (radius < 1) radius=1;
  1281.             canvas.Circle(cursor_x,cursor_y,radius);
  1282.         break; case CNVSQUARE:
  1283.             int hside=atoi(parameter);
  1284.             if (hside < 1) hside=1;
  1285.             for (char* parm=parameter; *parm && *parm!=',' && *parm!='x' && *parm!='*'; parm++)
  1286.                 ;
  1287.             int vside=*parm ? atoi(parm+1) : hside;
  1288.             if (vside < 1) vside=hside;
  1289.             canvas.Rectangle(cursor_x,cursor_y,hside,vside);
  1290.         break; case CNVTEXT:
  1291.             canvas.Text(cursor_x,cursor_y,parameter,fontselector.Chosen());
  1292.         break; case CNVDELETE1:
  1293.             Shape* sh=canvas.FindShape(cursor_x,cursor_y);
  1294.             if (sh) {
  1295.                 canvas.DeleteShape(sh);
  1296.             }
  1297.         break; case CNVHELP:
  1298.             // Popup canvas help on top of canvas window.
  1299.             // (it's an overlayed help box).
  1300.             canvashelp.Do(Object(0).X(),Object(0).Y());
  1301.         break; case CNVPHELP:
  1302.             // Open canvas parameter help window.
  1303.             canvasphelp.Open();
  1304.         break; case CNVWRITE:
  1305.             canvas.WriteMetafile(parameter);
  1306.         break; default:
  1307.             GEMformiconwindow::DoItem(item,e);
  1308.         }
  1309.  
  1310.         return ContinueInteraction;
  1311.     }
  1312.  
  1313. private:
  1314.     Canvas canvas;
  1315.  
  1316.     GEMslidable cursor;
  1317.     GEMslider speed;
  1318.     GEMtextobject parameter;
  1319.     GEMform canvashelp;
  1320.     GEMformwindow canvasphelp;
  1321.     FontWindow& fontselector;
  1322. };
  1323.  
  1324. class TextWindow : public GEMformiconwindow {
  1325. public:
  1326.     TextWindow(GEMactivity& in, const GEMrsc& rsc, FontWindow& fntselector) :
  1327.         GEMformiconwindow(in,rsc,TEXTWIN,TEXTWINI),
  1328.         scrolltext(*this,TXTBOX,80,24),
  1329.         fontselector(fntselector)
  1330.     {
  1331.         scrolltext.st_load_fonts();
  1332.         SetName(" Text Window ");
  1333.     }
  1334.  
  1335.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  1336.     {
  1337.         switch (item) {
  1338.          case TXTCLEAR:
  1339.         break; case TXTWRAP:
  1340.         break; case TXTVISCURC:
  1341.         break; case TXTUP:
  1342.             scrolltext.ScrollText(0,-1);
  1343.         break; case TXTDOWN:
  1344.             scrolltext.ScrollText(0,+1);
  1345.         break; case TXTLEFT:
  1346.             scrolltext.ScrollText(-1,0);
  1347.         break; case TXTRIGHT:
  1348.             scrolltext.ScrollText(+1,0);
  1349.         break; case TXTHOME:
  1350.             GEMfont to_use(scrolltext,fontselector.Chosen());
  1351.             to_use.Use();
  1352.             scrolltext.Redraw();
  1353.         }
  1354.  
  1355.         return GEMformiconwindow::DoItem(item,e);
  1356.     }
  1357.  
  1358. private:
  1359.     GEMscrolltext scrolltext;
  1360.     FontWindow& fontselector;
  1361. };
  1362.  
  1363. //
  1364. // Demonstrates GEMmenu as central to the interaction.
  1365. //
  1366. class Menu : GEMmenu {
  1367. public:
  1368.     Menu(GEMactivity& in, const GEMrsc& r) :
  1369.         GEMmenu(in,r,MENU),
  1370.         act(in),
  1371.         rsc(r),
  1372.         about(rsc,ABOUT),
  1373.         itisfree(rsc,FREE),
  1374.         authors(rsc,AUTHORS),
  1375.         gnu(rsc,GNU),
  1376.         flying(rsc),
  1377.         errwin(rsc,ERRWIN),
  1378.         clocknote(rsc,CLOCKNOTE),
  1379.         various(in,rsc),
  1380.         fonts(in,rsc),
  1381.         clock(in,rsc),
  1382.         windows(0),
  1383.         partsform(in,rsc),
  1384.         canvaswin(in,rsc,fonts),
  1385.         textwin(in,rsc,fonts)
  1386.     {
  1387.     }
  1388.  
  1389.     ~Menu()
  1390.     {
  1391.         GEMringfiw::DeleteAll(windows);
  1392.     }
  1393.  
  1394. protected:
  1395.     virtual GEMfeedback DoItem(int item, const GEMevent& e)
  1396.     {
  1397.         switch (item) {
  1398.          case DOABOUT:
  1399.             about.Do();
  1400.         break; case DOFREE:    
  1401.             itisfree.Do();
  1402.         break; case DOAUTHORS:
  1403.             authors.Do();
  1404.         break; case DOGNU:
  1405.             gnu.Do();
  1406.         break; case DOQUIT:
  1407.             return EndInteraction;
  1408.         break; case DOVARIOUS:
  1409.             various.Open();
  1410.         break; case DOFLYING:
  1411.             flying.Do();
  1412.         break; case DOBIGFORM:
  1413.             partsform.Open();
  1414.         break; case DOSHARING:
  1415.             if (!GEMringfiw::OpenNew(act,rsc,SHARING,SHARINGI,windows))
  1416.                 switch (errwin.Alert()) {
  1417.                  case 1: return DoItem(item,e);
  1418.                 break; case 2: about.Do();
  1419.                 }
  1420.         break; case DOFONTS:
  1421.             fonts.Open();
  1422.         break; case DOCANVAS:
  1423.             canvaswin.Open();
  1424.         break; case DOTEXTWIN:
  1425.             textwin.Open();
  1426.         break; case DOCLOCK:
  1427.             clocknote.Alert();
  1428.             clock.Open();
  1429.         }
  1430.  
  1431.         return ContinueInteraction;
  1432.     }
  1433.  
  1434. private:
  1435.     GEMringfiw* windows;
  1436.     GEMactivity& act;
  1437.     GEMrsc& rsc;
  1438.     FontWindow fonts; // defined before othrs, because it loads fonts.
  1439.     Clock clock;
  1440.     GEMform about,itisfree,authors,gnu;
  1441.     Flying flying;
  1442.     PartsForm partsform;
  1443.     CanvasWindow canvaswin;
  1444.     Various various;
  1445.     TextWindow textwin;
  1446.     GEMalert errwin;
  1447.     GEMalert clocknote;
  1448. };
  1449.  
  1450.  
  1451. //
  1452. // Demonstrates GEMfileselector
  1453. //
  1454. class FileChooser : public GEMobject, public GEMfileselector {
  1455. public:
  1456.     FileChooser(GEMform& f, int RSCibutn, GEMobject& disp, char* prmpt) :
  1457.         GEMobject(f,RSCibutn),
  1458.         GEMfileselector(disp.Text()),
  1459.         display(disp),
  1460.         prompt(prmpt)
  1461.     {
  1462.         display.Redraw();
  1463.     }
  1464.  
  1465.     virtual GEMfeedback Touch(int x, int y, const GEMevent& e)
  1466.     {
  1467.         if (Get(prompt)) display.Redraw();
  1468.  
  1469.         return ContinueInteraction;
  1470.     }
  1471.  
  1472. private:
  1473.     GEMobject& display;
  1474.     char* prompt;
  1475. };
  1476.  
  1477. //
  1478. // Demonstrates GEMdesktop
  1479. //
  1480. class Desktop : public GEMdesktop {
  1481. public:
  1482.     Desktop(GEMactivity& in, const GEMrsc& rsc) :
  1483.         GEMdesktop(in,rsc,DESKTOP),
  1484.         display(*this,FILEDISPLAY),
  1485.         files(*this,FILES,display,rsc.String(FILEPROMPT))
  1486.     {
  1487.     }
  1488.  
  1489. private:
  1490.     GEMobject display;
  1491.     FileChooser files;
  1492. };
  1493.  
  1494.  
  1495. class KeyProcessor : public GEMkeysink {
  1496. public:
  1497.     KeyProcessor(GEMactivity& a) :
  1498.         GEMkeysink(a),
  1499.         act(a)
  1500.     {
  1501.     }
  1502.  
  1503.     virtual GEMfeedback Consume(const GEMevent& ev)
  1504.     {
  1505.         switch (ev.Key()>>8) {
  1506.          case KEY_Q:
  1507.             return EndInteraction;
  1508.         break; case KEY_D:
  1509.             act.Dump();
  1510.         }
  1511.         return ContinueInteraction;
  1512.     }
  1513.  
  1514. private:
  1515.     GEMactivity& act;
  1516. };
  1517.  
  1518.  
  1519. main()
  1520. {
  1521.     // Before we do ANYTHING with GEM, we must declare a GEMapplication.
  1522.     GEMapplication example;
  1523.  
  1524.     // Next, we declare a GEMrsc, which we then use to create other objects.
  1525.     GEMrsc rsc("example.rsc",8,16);
  1526.     // GEMrsc rsc("example.rsc");
  1527.  
  1528.     if (!rsc) {
  1529.         // If the GEMrsc file cannot be created, we have to resort to a
  1530.         // GEMalert constructed from strings rather than from the GEMrsc.
  1531.         GEMalert dammit("Could not load \"example.rsc\".","Quit");
  1532.         dammit.Alert();
  1533.     } else {
  1534.         // But if everything goes fine, we create a GEMactivity, and
  1535.         // our own Menu and Desktop (see definitions above) in that
  1536.         // GEMactivity.
  1537.  
  1538.         GEMactivity activity;
  1539.  
  1540.         Menu menu(activity,rsc);
  1541.  
  1542.         KeyProcessor keyproc(activity);
  1543.  
  1544.         Desktop desktop(activity,rsc);
  1545.  
  1546.         // Then, conduct the GEMactivity - returns when all done.
  1547.         activity.Do();
  1548.     }
  1549. }
  1550.